"""
数据包服务
创建时间：2025-09-12
作者：开发团队
目的：处理数据包生成和管理相关业务逻辑
"""

from sqlalchemy.orm import Session
from sqlalchemy.exc import SQLAlchemyError
import json
import os
import hashlib
from datetime import datetime, timezone
from ..models.data_package import DataPackage
from ..utils.crypto import generate_key, encrypt_data
from ..utils.logger import get_logger

# 获取日志记录器
logger = get_logger(__name__)

# 创建数据包目录
DATA_PACKAGE_DIR = "data_packages"
if not os.path.exists(DATA_PACKAGE_DIR):
    os.makedirs(DATA_PACKAGE_DIR)


def create_data_package(db: Session, created_by: int, content_types: list[str]) -> DataPackage:
    """
    创建数据包
    :param db: 数据库会话
    :param created_by: 创建人ID
    :param content_types: 包含的数据类型
    :return: 创建的数据包对象
    """
    try:
        # 生成数据包内容（示例数据）
        package_content = generate_package_content(content_types)
        
        # 生成文件名
        timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
        filename = f"data_package_{timestamp}.json"
        file_path = os.path.join(DATA_PACKAGE_DIR, filename)
        
        # 生成签名
        signature = hashlib.sha256(json.dumps(package_content).encode('utf-8')).hexdigest()
        
        # 加密数据
        encryption_key = generate_key()
        encrypted_content = encrypt_data(json.dumps(package_content), encryption_key)
        
        # 保存加密数据到文件
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(encrypted_content)
        
        # 创建数据包对象
        package = DataPackage(
            content_types=json.dumps(content_types),
            file_path=file_path,
            file_size=os.path.getsize(file_path),
            encryption_status="encrypted",
            created_by=created_by
        )
        
        # 保存到数据库
        db.add(package)
        db.commit()
        db.refresh(package)
        
        # 保存密钥和签名信息（实际项目中应存储在安全的地方）
        key_file_path = f"{file_path}.key"
        signature_file_path = f"{file_path}.sig"
        
        with open(key_file_path, 'wb') as f:
            f.write(encryption_key)
        
        with open(signature_file_path, 'w') as f:
            f.write(signature)
        
        logger.info(f"数据包创建成功: ID={package.id}, 文件={filename}")
        return package
    except SQLAlchemyError as e:
        db.rollback()
        logger.error(f"创建数据包失败: {str(e)}")
        raise ValueError("创建数据包失败")
    except Exception as e:
        db.rollback()
        logger.error(f"创建数据包异常: {str(e)}")
        raise ValueError("创建数据包失败")


def get_data_packages(db: Session, skip: int = 0, limit: int = 100) -> list[DataPackage]:
    """
    获取数据包列表
    :param db: 数据库会话
    :param skip: 跳过的记录数
    :param limit: 返回的记录数
    :return: 数据包列表
    """
    try:
        return db.query(DataPackage).offset(skip).limit(limit).all()
    except SQLAlchemyError as e:
        logger.error(f"查询数据包列表失败: {str(e)}")
        raise ValueError("查询数据包列表失败")


def get_data_package_by_id(db: Session, package_id: int) -> DataPackage | None:
    """
    根据ID获取数据包
    :param db: 数据库会话
    :param package_id: 数据包ID
    :return: 数据包对象或None
    """
    try:
        return db.query(DataPackage).filter(DataPackage.id == package_id).first()
    except SQLAlchemyError as e:
        logger.error(f"查询数据包失败: {str(e)}")
        return None


def delete_data_package(db: Session, package_id: int) -> bool:
    """
    删除数据包
    :param db: 数据库会话
    :param package_id: 数据包ID
    :return: 删除是否成功
    """
    try:
        package = get_data_package_by_id(db, package_id)
        if not package:
            return False
        
        # 获取文件路径
        file_path = str(package.file_path)
        
        # 删除文件
        if os.path.exists(file_path):
            os.remove(file_path)
        
        # 删除密钥文件
        key_file_path = f"{file_path}.key"
        if os.path.exists(key_file_path):
            os.remove(key_file_path)
        
        # 删除签名文件
        signature_file_path = f"{file_path}.sig"
        if os.path.exists(signature_file_path):
            os.remove(signature_file_path)
        
        # 从数据库删除记录
        db.delete(package)
        db.commit()
        
        logger.info(f"数据包删除成功: ID={package_id}")
        return True
    except Exception as e:
        db.rollback()
        logger.error(f"删除数据包异常: {str(e)}")
        return False


def download_data_package(db: Session, package_id: int) -> dict[str, object] | None:
    """
    下载数据包
    :param db: 数据库会话
    :param package_id: 数据包ID
    :return: 数据包下载信息或None
    """
    try:
        package = get_data_package_by_id(db, package_id)
        if not package:
            return None
        
        # 增加下载次数
        current_count = package.download_count or 0
        setattr(package, 'download_count', current_count + 1)
        db.commit()
        
        # 获取文件路径
        file_path = str(package.file_path)
        
        # 返回下载信息
        return {
            "id": package.id,
            "file_path": file_path,
            "file_name": os.path.basename(file_path),
            "file_size": package.file_size,
            "download_url": f"/api/superadmin/data-packages/{package_id}/download"
        }
    except Exception as e:
        db.rollback()
        logger.error(f"下载数据包异常: {str(e)}")
        return None


def generate_package_content(content_types: list[str]) -> dict[str, object]:
    """
    生成数据包内容（示例实现）
    :param content_types: 包含的数据类型
    :return: 生成的数据包内容
    """
    # 根据内容类型生成示例数据
    content: dict[str, object] = {}
    
    if "users" in content_types:
        content["users"] = [
            {"id": 1, "username": "admin", "name": "管理员", "role": "admin"},
            {"id": 2, "username": "guard1", "name": "门卫1", "role": "guard"}
        ]
    
    if "campuses" in content_types:
        content["campuses"] = [
            {"id": 1, "name": "主校区", "address": "北京市朝阳区"},
            {"id": 2, "name": "分校区", "address": "北京市海淀区"}
        ]
    
    if "special_visitors" in content_types:
        content["special_visitors"] = [
            {"id": 1, "name": "VIP客户", "type": "VIP", "valid_until": "2025-12-31"},
            {"id": 2, "name": "合作方", "type": "其他", "valid_until": "2025-12-31"}
        ]
    
    return content